home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Best of MacTutor - S…e Code for Volumes 1 to 5
/
The Best of MacTutor - Source Code for Volume 1-5 (Wayzata Technology)(6031)(1990).bin
/
Source Code
/
#49 (Oct 89)
/
SC #49.sit
/
Driver Code
/
Pdriver.a
< prev
next >
Wrap
Text File
|
1988-01-12
|
15KB
|
568 lines
TITLE 'Pump driver'
BLANKS ON
CASE ON
String Pascal
Machine MC68020
PRINT OFF
INCLUDE 'Traps.a'
INCLUDE 'ToolEqu.a'
INCLUDE 'QuickEqu.a'
INCLUDE 'SysEqu.a'
INCLUDE 'SysErr.a'
INCLUDE 'TimeEqu.a'
INCLUDE 'Quickdraw.inc'
INCLUDE 'GNEGlobals.inc'
INCLUDE 'PumpErrs.inc'
LOAD 'ProgStrucMacs.d'
LOAD 'FlowCtlMacs.d'
LOAD 'DRVRStruct.d'
PRINT ON
IMPORT HexSet,IntegerSet,AskFormat,RespondToRead
IMPORT SerialComplete,TimeOutInstall
ParamBlockSize equ 108
ActionOffset equ csParam
RequestOffset equ csParam+2
InfoOffset equ csParam+4
DCEPtr equ csParam+6
IntegerFmt equ 0
HexFmt equ 1
Ask equ 0
Set equ 1
S_request equ 0
R_request equ 1
V_request equ 2
I_request equ 3
baud2400 equ 46
stop10 equ 16384
noParity equ 0
data8 equ 3072
EnabledFlags equ (1<<dStatEnable) + (1<<dCtlEnable) + (1<<dNeedTime)
EventMask equ 1<<app1Evt ; Handle only driver events
Owned equ %1100000000000000
DrvrStorage EQU A3
StringAddress EQU A1
DStore Record 0
WriteUnit DS.W 1
ReadUnit DS.W 1
TimeCount DS.L 1
KillBlock DS.L 1
KillTask DS.L 1
IncomingLength DS.B 1
IncomingString DS.B 9
Align 2
TableOffset equ *
TableLen DS.W 1
Table DS.W 5
ENDR
IOString Record 0
OutgoingString DS.B 8
ReadBuffer DS.B 1
Align 2
StringAllocation equ *
ENDR
EvRecord Record 0
myEvent DS.L EventRecord ; Current event info
ENDR
DRVRStart DRVREntry
DRVRBegin Save=A2-A4/D1-D2,with=(DStore,IOString,GNEGlobals,EvRecord);
DC.B EnabledFlags
DC.B 0 ; Lower byte is unused
DC.W 7*60 ; periodic actions every 7 seconds
DC.W EventMask
DC.W 0 ; No menu for this accessory
; org drvrOpen
DC.W DRVROpen ; Open routine
DC.W DRVRPrime ; Prime
DC.W DRVRControl ; Control
DC.W DRVRStatus ; Status
DC.W DRVRClose ; Close
DRVRTitle
DC.B 'PumpDriver' ; Driver Name
ALIGN 2 ; Word align
DRVROpen DRVREnter
MOVE.L A1,A2
* Load the pump request table from the resource fork
ResHandle equ A4
Call _GetNamedResource:L ( #'PDDF':L , #'Table':A ),ResHandle:L
EXG A0,ResHandle
_HLock
_GetHandleSize
If# D0 LT.L #0 Then.S
MOVE.W #openErr,ioResult(ResHandle)
Return
EndIf#
EXG A0,ResHandle
MOVE.L D0,D1
MOVE.L A0,-(SP)
* Get a new handle to put the Driver globals and the pump request table into
MOVE.L A1,-(SP)
ADD.L #TableOffset,D0
_NewHandle ,clear
_HLock
MOVE.L A0,dCtlStorage(A2)
MOVE.L (A0),A1
MOVE.L A1,DrvrStorage
ADD.L #TableOffset,A1
MOVE.L (ResHandle),A0
MOVE.L D1,D0
_BlockMove
Call _ReleaseResource ( ResHandle:L )
MOVE.L (SP)+,A1
* Open the serial drivers and set them up for the pump
MOVE.L #ParamBlockSize,D0
_NewPtr ,clear
LEA #'.AOUT',A2
MOVE.L A2,ioNamePtr(A0)
MOVE.B #fsWrPerm,ioPermssn(A0)
_Open
If# D0 NE.W #noErr Then.S
MOVE.L (SP)+,A0
MOVE.W #openErr,ioResult(A0)
Return
EndIf#
MOVE.W ioRefNum(A0),WriteUnit(DrvrStorage)
* Set the output port for 2400 baud, 1 stop, no parity, eight bit communication
PortSetting equ baud2400+stop10+noParity+data8
MOVE.W #PortSetting,csParam(A0)
MOVE.W #8,csCode(A0)
_Control
* Open the input port
LEA #'.AIN',A2
MOVE.L A2,ioNamePtr(A0)
MOVE.B #fsRdPerm,ioPermssn(A0)
_Open
If# D0 NE.W #noErr Then.S
MOVE.L (SP)+,A0
MOVE.W #openErr,ioResult(A0)
Return
EndIf#
MOVE.W ioRefNum(A0),ReadUnit(DrvrStorage)
* Set the input port for 2400 baud, 1 stop, no parity, eight bit communication
MOVE.W #PortSetting,csParam(A0)
MOVE.W #8,csCode(A0)
_Control
_KillIO
_DisposPtr
* Set up a parameter block for the serial IO timeout function. The pointer is stored in the
* Driver private storage. Also insert a task into the time manager queue which performs the
* IO timeout function.
MOVE.L #ParamBlockSize,D0
_NewPtr ,clear
MOVE.L A0,KillBlock(DrvrStorage)
MOVE.L #1500,TimeCount(DrvrStorage)
MOVE.L #tmQSize,D0
_NewPtr
MOVE.L A0,KillTask(DrvrStorage)
Call TimeOutInstall ( A0:L , KillBlock(DrvrStorage):L )
* Add a routine to the GNEFilter chain to handle calling the pump driver with appropriate
* driver events. The filter rouitne is stored in a resource of type 'GNEF',
* named 'GNEFilter', and is accessed by name so that multiple drivers can
* use the same filter. The resource must be locked & nonpurgeable, but should
* not be preloaded. The driver gets a handle to the resource, dereferences
* it, and checks the Drvr_num field. If the value there is zero, then we
* store the value in the system global JGNEFilter in the longword above
* the entry point for our GNE filter, and place the GNE filter pointer in
* JGNEFilter. The address of our GNE filter is found by adding the offset
* found in the first word of the resource to the dereferenced handle. We
* also allocate a pointer for the filter's use and store it at the offset
* Control_Ptr. Whether or not the value of Drvr_num was zero, we then check
* to make sure the driver reference number is not already in the list, and if
* not, increment Drvr_num and add the driver's reference number to the list
* of cooperating drivers.
Call _GetNamedResource:L ( #'GNEF':L , #'GNEFilter':A ),A2
CMPA #0,A2
If# NE Then.S
MOVE.L (A2),A2
MOVE.W Drvr_num(A2),D1
If# D1 EQ.W #0 Then.S
MOVE.W (A2),D0
LEA (A2,D0.W),A3
MOVE.L JGNEFilter,-4(A3)
MOVE.L A3,JGNEFilter
MOVE.L #ParamBlockSize,D0
_NewPtr ,clear
MOVE.L A0,Control_Ptr(A2)
EndIf#
MOVE.W dCtlRefNum(A1),D2
If# D1 LE #entry_slots Then.S
For# D1 DownTo #1 Do.S
If# Drvr_num(A2,D1.W*2) EQ.W D2 Then.S
GoTo#.S DuplicateDrvr
EndIf#
EndF#
ADD.W #1,Drvr_num(A2)
MOVE.W Drvr_num(A2),D1
MOVE.W D2,Drvr_num(A2,D1.W*2)
Else#.S
* Get the Resource ID for the driver to calculate the ID of owned Alert (sub ID 0)
LEA DRVREntry,A0
_RecoverHandle
LINK A6,#-12
Call _GetResInfo ( A0:L , (A6):A , 4(A6):A , 8(A6):L )
MOVE.W (A6),D2
UNLK A6
MULU #32,D2
ADD.W #Owned,D2
Call _CautionAlert:W ( D2:W , 0:L ),CC
EndIf#
EndIf#
DuplicateDrvr:
MOVE.L (SP)+,A0
MOVE.W #openErr,ioResult(A0)
Return
DRVRClose DRVREnter
MOVE.L A0,-(SP)
MOVE.L dCtlStorage(A1),A2
MOVE.L (A2),DrvrStorage
MOVE.L #ParamBlockSize,D0
_NewPtr ,clear
MOVE.W ReadUnit(DrvrStorage),ioRefNum(A0)
_Close
MOVE.W WriteUnit(DrvrStorage),ioRefNum(A0)
_Close
_DisposPtr
MOVE.L KillTask(DrvrStorage),A0
_RmvTime
_DisposPtr
MOVE.L KillBlock(DrvrStorage),A0
_DisposPtr
Move.L A2,A0
_DisposHandle
MOVE.W dCtlRefNum(A1),D2
Call _GetNamedResource:L ( #'GNEF':L , #'GNEFilter':A ),A2
CMPA #0,A2
If# NE Then.S
MOVE.L (A2),A3
MOVE.W Drvr_num(A3),D1
If# D1 GT.W #1 Then.S
While# D2 NE.W Drvr_num(A3,D1.W*2) Do.S
DBEQ.W D1,DuplicateClose
EndW#
If# D1 NE.W Drvr_num(A3) Then.S
ADD.W #1,D1
For# D1 To Drvr_num(A3) Do.S
LEA Drvr_num(A3,D1.W*2),A4
MOVE.W (A4),-2(A4)
EndF#
EndIf#
SUBQ.W #1,Drvr_num(A3)
ElseIf#.S D1 EQ.W #1 Then.S
If# D2 NE.W Drvrentries(A3) Then.S
GoTo#.S DuplicateClose
EndIf#
MOVE.L GNE_Next(A3),JGNEFilter
Call _ReleaseResource ( A2:L )
EndIf#
EndIf#
DuplicateClose:
MOVE.W #noErr,D0
MOVE.L (SP)+,A0
DRVRExit ioTrap(A0)
DRVRPrime DRVREnter
MOVE.W noErr,D0
Return
DRVRStatus DRVREnter
* First, check to see if this is an immediate call. If so, it "jumps the line", and will be the
* next call serviced after the current one (if any) is completed.
MOVE.W ioTrap(A0),D1
BTST #noQueueBit,D1
If# NE Then.S ;An Immediate call
LEA dCtlQHead(A1),A2
CMP.L #0,(A2)
If# EQ Then.S ;The queue is empty
_Status ,async
Return
EndIf#
MOVE.L (A2),D1
CMP.L 4(A2),D1
If# EQ Then.S ;The queue has only one entry
_Status ,async
Return
EndIf#
MOVE.W ioTrap(A0),D1
BCLR #noQueueBit,D1 ;Fool the driver into thinking this
BSET #asyncTrpBit,D1 ;is an asynchronous queue entry
MOVE.W D1,ioTrap(A0)
MOVE.W #1,ioResult(A0)
MOVE.L (A2),A2 ;Get parameter block of queue head
MOVE.L (A2),(A0) ;Put qLink of qHead in current parameter block
MOVE.L A0,(A2) ;and replace it with current parameter block pointer
Return
EndIf#
MOVE.L A0,A2
MOVE.L dCtlStorage(A1),A4
MOVE.L (A4),A4
MOVE.L A1,DCEPtr(A2)
MOVE.W TableLen(A4),D1
MOVE.W RequestOffset(A2),D0
If# D0 GT.W D1 Then.S ;Not a valid pump call
MOVE.W #statusErr,ioResult(A2)
MOVE.L A2,D0
MOVE.W #app2Evt,A0
_PostEvent
MOVE.L A2,A0
MOVE.L #statusErr,D0
DRVRExit ioTrap(A0)
EndIf#
* Get some storage for the string we will build
MOVE.L #StringAllocation,D0
_NewPtr ,clear
MOVE.L A0,A3
* Get some storage for the parameter block that is used for serial driver calls
MOVE.L #ParamBlockSize,D0
_NewPtr ,clear
MOVE.L A3,ioBuffer(A0)
MOVE.L A2,ioMisc(A0)
* Now, get the string formatting info out of the Driver private storage
MOVE.W WriteUnit(A4),ioRefNum(A0)
MOVE.W RequestOffset(A2),D0
ADD.W D0,D0
Add.W #TableOffset+2,D0
MOVE.L A0,-(SP)
If# ActionOffset(A2) EQ.W #Ask Then.S
Call AskFormat ( A3:L , ioReqCount(A0):A , (A4,D0.W):B )
ElseIf#.S 1(A4,D0.W) EQ.B #IntegerFmt Then.S
Call IntegerSet ( A3:L , ioReqCount(A0):A , (A4,D0.W):B ,\
InfoOffset(A2):W )
Else#.S
Call HexSet ( A3:L , ioReqCount(A0):A , (A4,D0.W):B ,\
InfoOffset(A2):W )
EndIf#
MOVE.L (SP)+,A0
LEA SerialComplete,A3
MOVE.L A3,ioCompletion(A0)
_Write ,async
* Set up the time manager task to KillIO on the channel in TimeCount milliseconds. KillBlock
* is a previously allocated parameter block, which is passed to TimeOutInstall and stored
* locally (that is, in-line in the code). This is necessary because the routine executes at
* interrupt level, and is part of a driver, and thus cannot use the memory manager or application
* globals.
MOVE.L KillBlock(A4),A0
MOVE.L WriteUnit(A4),ioRefNum(A0)
MOVE.L KillTask(A4),A0
MOVE.L TimeCount(A4),D0
_PrimeTime
MOVE.L #noErr,D0
Return
DRVRControl DRVREnter
If# csCode(A0) EQ.W #accEvent Then
MOVE.L csParam(A0),A2
MOVE.L myEvent.message(A2),A2
MOVE.W ioTrap(A2),D2
AND.W #$00FF,D2
MOVE.W ioResult(A2),D3
* Check to see the serial call was not aborted by KillIO. If so, inform the application,
* dequeue the pump driver call, and return
If# D3 EQ.W #abortErr Then.S
MOVE.L ioMisc(A2),A0
EXG A0,A2
_DisposPtr
MOVE.W #TimeOut,ioResult(A2)
ADD.W D2,ioResult(A2)
MOVE.L A2,D0
MOVE.W #app2Evt,A0
_PostEvent
MOVE.L #TimeOut,D0
MOVE.L A2,A0
DRVRExit ioTrap(A0)
EndIf#
* The serial call did not time out, so figure out if it was a READ or a WRITE by examining
* the low nybble of the trap field of the parameter block.
If# D2 EQ.W #aRdCmd Then
* READ - examine the character read. If it is a '*', the new pump is just wasting our valuable
* time, and so we ignore it and queue another read. If it is a carraige return, we call
* RespondToRead to interpret the completed pump response. Any other character is
* incorporated into the string being built at IncomingString and increment the
* IncomingLength byte, and another READ is queued. If the byte exceeds 8 characters, we
* have a problem.
MOVE.L ioBuffer(A2),A3
MOVE.B (A3),D1
MOVE.L dCtlStorage(A1),A4
MOVE.L (A4),A4
If# D1 EQ.B #13 Then.S
MOVE.L KillTask(A4),A0
CLR.L tmCount(A0)
MOVE.L ioMisc(A2),A3
Call RespondToRead
MOVE.L ioMisc(A2),A0
EXG A0,A2
_DisposPtr
MOVE.L A2,D0
MOVE.W #app2Evt,A0
_PostEvent
CLR.B IncomingLength(A4)
CLR.L IncomingString(A4)
CLR.L IncomingString+4(A4)
CLR.L D0
MOVE.L A2,A0
DRVRExit ioTrap(A0)
ElseIf#.S D1 EQ.B #'*' Then.S
MOVE.L A2,A0
_Read ,async
Return
Else#.S
MOVE.B IncomingLength(A4),D0
EXT.W D0
MOVE.B D1,IncomingString(A4,D0.W)
ADD.W #1,D0
If# D0 LE.W #8 Then.S
MOVE.B D0,IncomingLength(A4)
Else#.S
MOVE.L KillBlock,A0
MOVE.W ReadUnit(A4),ioRefNum(A0)
_KillIO
MOVE.L ioMisc(A2),A0
EXG A0,A2
_DisposPtr
MOVE.W #TooManyCharacters,ioResult(A2)
MOVE.L A2,D0
MOVE.W #app2Evt,A0
_PostEvent
MOVE.L A2,A0
MOVE.L #TooManyCharacters,D0
DRVRExit ioTrap(A0)
EndIf#
MOVE.L A2,A0
_Read ,async
Return
EndIf#
ElseIf#.S D2 EQ.W #aWrCmd Then.S
MOVE.L dCtlStorage(A1),A4
MOVE.L (A4),A4
MOVE.L KillBlock(A4),A0
MOVE.W ReadUnit(A4),ioRefNum(A0)
MOVE.L A2,A0
MOVE.W ReadUnit(A4),ioRefNum(A0)
MOVE.L #1,ioReqCount(A0)
MOVE.L ioBuffer(A0),A2
LEA ReadBuffer(A2),A2
MOVE.L A2,ioBuffer(A0)
LEA SerialComplete,A4
MOVE.L A4,ioCompletion(A0)
_Read ,async
Return
EndIf#
ElseIf#.S csCode(A0) EQ.W #accRun Then.S
MOVE.L A0,-(SP)
MOVE.L #ParamBlockSize,D0
_NewPtr ,clear
MOVE.W dCtlRefNum(A1),ioRefNum(A0)
MOVE.W #S_request,RequestOffset(A0)
MOVE.W #Ask,ActionOffset(A0)
CLR.L ioCompletion(A0)
_Status ,async
MOVE.L (SP)+,A0
ElseIf#.S csCode(A0) EQ.W #killCode Then.S
MOVE.L #ParamBlockSize,D0
MOVE.L dCtlStorage(A1),A2
MOVE.L (A2),A2
_NewPtr ,clear
MOVE.W ReadUnit(A2),ioRefNum(A0)
_KillIO
MOVE.W WriteUnit(A2),ioRefNum(A0)
_KillIO
_DisposPtr
Return
ElseIf#.S csCode(A0) EQ.W #goodBye Then.S
JMP DRVRClose
EndIf#
MOVE.L #noErr,D0
DRVRExit ioTrap(A0)
ENDP
END